Categories
MongoDB

Using MongoDB with Mongoose — Discriminators

Spread the love

To make MongoDB database manipulation easy, we can use the Mongoose NPM package to make working with MongoDB databases easier.

In this article, we’ll look at how to use Mongoose to manipulate our MongoDB database.

Discriminators

Discriminators are a schema inheritance mechanism.

They let us enable multiple models to have overlapping schemas on top of the same MongoDB collection.

For example, we can use them as follows:

async function run() {
  const { createConnection, Types, Schema } = require('mongoose');
  const db = createConnection('mongodb://localhost:27017/test');
  const options = { discriminatorKey: 'kind' };

  const eventSchema = new Schema({ time: Date }, options);
  const Event = db.model('Event', eventSchema);
  const ClickedLinkEvent = Event.discriminator('ClickedLink',
    new Schema({ url: String }, options));
  const genericEvent = new Event({ time: Date.now(), url: 'mongodb.com' });
  console.log(genericEvent)

  const clickedEvent =
    new ClickedLinkEvent({ time: Date.now(), url: 'mongodb.com' });
  console.log(clickedEvent)
}
run();

We created an Event model from the eventSchema .

It has the discriminatorKey so that we get can discriminate between the 2 documents we create later.

To create the ClickedLinkEvent model, we call Event.discriminator to create a model by inheriting from the Event schema.

We add the url field to the ClickedLinkEvent model.

Then when we add the url to the Event document and the ClickedLinkEvent document, only the clickedEvent object has the url property.

We get:

{ _id: 5f6f78f17f83ca22408eb627, time: 2020-09-26T17:22:57.690Z }

as the value of genericEvent and:

{
  _id: 5f6f78f17f83ca22408eb628,
  kind: 'ClickedLink',
  time: 2020-09-26T17:22:57.697Z,
  url: 'mongodb.com'
}

as the value of clickedEvent .

Discriminators Save to the Model’s Collection

We can save different kinds of events all at once.

For example, we can write:

async function run() {
  const { createConnection, Types, Schema } = require('mongoose');
  const db = createConnection('mongodb://localhost:27017/test');
  const options = { discriminatorKey: 'kind' };

  const eventSchema = new Schema({ time: Date }, options);
  const Event = db.model('Event', eventSchema);

  const ClickedLinkEvent = Event.discriminator('ClickedLink',
    new Schema({ url: String }, options));

  const SignedUpEvent = Event.discriminator('SignedUp',
    new Schema({ user: String }, options));

  const event1 = new Event({ time: Date.now() });
  const event2 = new ClickedLinkEvent({ time: Date.now(), url: 'mongodb.com' });
  const event3 = new SignedUpEvent({ time: Date.now(), user: 'mongodbuser' });
  await Promise.all([event1.save(), event2.save(), event3.save()]);
  const count = await Event.countDocuments();
  console.log(count);
}
run();

We created the eventSchema as an ordinary schema.

And the rest of the models are created from the Event.discriminator method.

Then we created the models and saved them all.

And finally, we called Event.countDocuments to get the number of items saved under the Event model.

Then count should be 3 since ClickedLinkEvent and SignedUpEvent both inherit from Event itself.

Discriminator Keys

We can tell the difference between each type of models with the __t property by default.

For instance, we can write:

async function run() {
  const { createConnection, Types, Schema } = require('mongoose');
  const db = createConnection('mongodb://localhost:27017/test');

  const eventSchema = new Schema({ time: Date });
  const Event = db.model('Event', eventSchema);

  const ClickedLinkEvent = Event.discriminator('ClickedLink',
    new Schema({ url: String }));

  const SignedUpEvent = Event.discriminator('SignedUp',
    new Schema({ user: String }));

  const event1 = new Event({ time: Date.now() });
  const event2 = new ClickedLinkEvent({ time: Date.now(), url: 'mongodb.com' });
  const event3 = new SignedUpEvent({ time: Date.now(), user: 'mongodbuser' });
  await Promise.all([event1.save(), event2.save(), event3.save()]);
  console.log(event1.__t);
  console.log(event2.__t);
  console.log(event3.__t);
}
run();

to get the type of data that’s saved from the console logs. We should get:

undefined
ClickedLink
SignedUp

logged.

We can add the discriminatorKey in the options to change the discriminator key.

So we can write:

async function run() {
  const { createConnection, Types, Schema } = require('mongoose');
  const db = createConnection('mongodb://localhost:27017/test');
  const options = { discriminatorKey: 'kind' };
  const eventSchema = new Schema({ time: Date }, options);
  const Event = db.model('Event', eventSchema);

const ClickedLinkEvent = Event.discriminator('ClickedLink',
    new Schema({ url: String }, options));

const SignedUpEvent = Event.discriminator('SignedUp',
    new Schema({ user: String }, options));

const event1 = new Event({ time: Date.now() });
  const event2 = new ClickedLinkEvent({ time: Date.now(), url: 'mongodb.com' });
  const event3 = new SignedUpEvent({ time: Date.now(), user: 'mongodbuser' });
  await Promise.all([event1.save(), event2.save(), event3.save()]);
  console.log(event1.kind);
  console.log(event2.kind);
  console.log(event3.kind);
}
run();

to set the options for each model and then access the kind property instead of __t and get the same result as before.

Conclusion

We can use discriminators to create new models by inheriting one model from another.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *